package com.free.semantic.core.impl;

import com.free.semantic.common.*;
import com.free.semantic.core.ConceptInstanceManage;
import com.free.semantic.core.ConceptManage;
import com.free.semantic.core.FunctionProvider;
import com.free.semantic.core.RelationFunction;
import com.free.semantic.core.concept.SubConceptManage;
import com.free.semantic.core.funcimpl.BaseFuncImpl;
import com.free.semantic.core.funcimpl.MergeFuncImpl;
import com.free.semantic.core.proxy.ProxyFunctionProvider;
import com.free.semantic.utils.Utils;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * 获取全量概念信息 执行计算逻辑 注册函数提供器
 */
public class ConceptInstanceManageImpl implements ConceptInstanceManage {

    private ConceptManage conceptManage;

    private FunctionProvider functionProvider;

    private Map<RelationItem, List<RelationFunction>> relationFunctionMap = new HashMap<>();

    private Map<String, FunctionProvider> functionProviderMap = new HashMap<>();

    public ConceptInstanceManageImpl(ConceptManage conceptManage) {
        this.conceptManage = conceptManage;
        SubConceptManage.appendSubConceptImpl(this);
    }

    public ConceptInstanceManageImpl(ConceptManage conceptManage, FunctionProvider... functionProvider) {
        this.conceptManage = conceptManage;
        SubConceptManage.appendSubConceptImpl(this);
        this.functionProvider = mergeFunc(functionProvider);
    }

    private FunctionProvider mergeFunc(FunctionProvider... functionProviders) {
        return relationItem -> {
            for (FunctionProvider functionProvider : functionProviders) {
                RelationFunction function = functionProvider.getFunction(relationItem);
                if (function != null) {
                    return function;
                }
            }
            return null;
        };
    }

    @Override
    public void registerFunction(List<String> sourceObj, String destObj, RelationFunction relationFunction) {
        if (CollectionUtils.isEmpty(sourceObj) || StringUtils.isBlank(destObj)) {
            throw new IllegalArgumentException("参数异常");
        }
        RelationItem relationItem = Utils.findSingleItem(conceptManage, sourceObj, destObj);
        if (!relationFunctionMap.containsKey(relationItem)) {
            relationFunctionMap.put(relationItem, new ArrayList<>());
        }
        if (relationFunction instanceof BaseFuncImpl) {
            ((BaseFuncImpl) relationFunction).setRelationItem(relationItem);
        }
        relationFunctionMap.get(relationItem).add(relationFunction);
    }

    @Override
    public void registerFunction(List<RangeObj> sourceObj, RangeObj destObj, RelationFunction relationFunction) {
        if (CollectionUtils.isEmpty(sourceObj) || destObj == null) {
            throw new IllegalArgumentException("参数异常");
        }
        List<RelationChain> relationChains = conceptManage.findExactRelationChain(sourceObj, destObj);
        if (relationChains.size() != 1) {
            throw new RuntimeException(String.format("来源事物为：%s,目标事物为：%s 的关系路径数量不为1", sourceObj, destObj));
        }
        List<RelationItem> items = relationChains.get(0).getRelationItems();
        if (items.size() != 1) {
            throw new RuntimeException("不允许夸关系建立实现");
        }
        RelationItem relationItem = relationChains.get(0).toRelationItem();
        List<String> list1 = relationItem.getSourceObjectList().stream().map(RangeObj::getObjectName).collect(Collectors.toList());
        List<String> list2 = Ls.of(relationItem.getDestObject().getObjectName());
        if (!stringEqual(list1, sourceObj.stream().map(RangeObj::getObjectName).collect(Collectors.toList())) || !stringEqual(list2, Lists.newArrayList(destObj.getObjectName()))) {
            throw new RuntimeException(String.format("不存在 来源事物为：%s,目标事物为：%s 的关系", sourceObj, destObj));
        }
        if (!relationFunctionMap.containsKey(relationItem)) {
            relationFunctionMap.put(relationItem, new ArrayList<>());
        }
        if (relationFunction instanceof BaseFuncImpl) {
            ((BaseFuncImpl) relationFunction).setRelationItem(relationItem);
        }
        relationFunctionMap.get(relationItem).add(relationFunction);
    }


    @Override
    public void registerFunction(String providerName, FunctionProvider functionProvider) {
        functionProviderMap.put(providerName, functionProvider);
    }

    @Override
    public void registerFunction(String providerName, ConceptInstanceManage implementModel) {
        ProxyFunctionProvider functionProvider = new ProxyFunctionProvider(this.conceptManage, implementModel);
        functionProvider.prepareFunctionMap();
        functionProviderMap.put(providerName, functionProvider);
    }

    @Override
    public List<String> getProviderLs() {
        return new ArrayList<>(functionProviderMap.keySet());
    }

    @Override
    public RelationFunction getFunction(List<RangeObj> sourceObj, RangeObj destObj) {
        List<RelationChain> relationChains = conceptManage.findExactRelationChain(sourceObj, destObj);
        if (relationChains.size() != 1) {
            throw new RuntimeException(String.format("来源事物为：%s,目标事物为：%s 的关系，存在的关系不唯一", sourceObj, destObj));
        }
        RelationChain chain = relationChains.get(0);
        if (existFunc(chain)) {
            if (chain.getRelationItems().size() == 1) {
                return getFunction(chain.getRelationItems().get(0));
            }
            return context -> {
                List<ParamNode> from = new ArrayList<>();
                for (RangeObj source : sourceObj) {
                    from.add(new ParamNode(source, context.getListValue(source)));
                }
                List<Object> res = calculateChain(from, chain, destObj.getObjectName());
                context.setValue(destObj, res);
            };
        } else {
            return null;
        }
    }

    @Override
    public RelationFunction getFunction(List<String> sourceObj, String destObj) {
        List<RelationChain> relationChains = conceptManage.findExactRelationChain(sourceObj, destObj);
        if (relationChains.size() != 1) {
            throw new RuntimeException(String.format("来源事物为：%s,目标事物为：%s 的关系，存在的关系不唯一", sourceObj, destObj));
        }
        RelationChain chain = relationChains.get(0);
        if (existFunc(chain)) {
            if (chain.getRelationItems().size() == 1) {
                return getFunction(chain.getRelationItems().get(0));
            }
            return context -> {
                List<ParamNode> from = new ArrayList<>();
                for (String source : sourceObj) {
                    from.add(new ParamNode(source, context.getListValue(source)));
                }
                List<Object> res = calculateChain(from, chain, destObj);
                context.setValue(destObj, res);
            };
        } else {
            return null;
        }
    }

    private boolean stringEqual(List<String> list1, List<String> list2) {
        return new HashSet<>(list1).equals(new HashSet<>(list2));
    }

    @Override
    public ConceptManage concept() {
        return conceptManage;
    }

    @Override
    public <T> List<T> takePath(List<ParamNode> from, String destObject) {
        if (CollectionUtils.isEmpty(from) || StringUtils.isBlank(destObject)) {
            throw new IllegalArgumentException();
        }
        List<String> fromObjList = from.stream().map(ParamNode::getNodeName).collect(Collectors.toList());
        List<RelationChain> relationChains = conceptManage.findExactRelationChain(fromObjList, destObject);
        if (CollectionUtils.isEmpty(relationChains)) {
            throw new RuntimeException("执行路径不存在");
        }
        if (relationChains.size() > 1) {
            throw new RuntimeException("执行路径不唯一");
        }
        return calculateChain(from, relationChains.get(0), destObject);
    }

    @Override
    public <T> List<T> takePath(List<ParamNode> from, RangeObj destObject) {
        if (CollectionUtils.isEmpty(from) || destObject == null) {
            throw new IllegalArgumentException();
        }
        List<RangeObj> fromObjList = from.stream().map(Utils::getNode).collect(Collectors.toList());
        List<RelationChain> relationChains = conceptManage.findExactRelationChain(fromObjList, destObject);
        if (CollectionUtils.isEmpty(relationChains)) {
            throw new RuntimeException("执行路径不存在");
        }
        if (relationChains.size() > 1) {
            throw new RuntimeException("执行路径不唯一");
        }
        return calculateChain(from, relationChains.get(0), destObject.getObjectName());
    }

    @Override
    public <T> List<T> goByLocatorNode(List<BaseNode> sourceObj, RangeObj destObj) {
        if (CollectionUtils.isEmpty(sourceObj) || destObj == null) {
            throw new IllegalArgumentException();
        }
        RelationChain chain = conceptManage.findChainByLocatorNode(sourceObj, destObj);
        if (chain == null) {
            throw new RuntimeException(String.format("查询关系 %s->%s 不存在", String.join(",", sourceObj.stream().map(node -> Utils.getNode(node).toString()).collect(Collectors.toList())), destObj.toString()));
        }
        List<ParamNode> paramNodes = filterToNodeInfo(sourceObj);
        return calculateChain(paramNodes, chain, destObj.getObjectName());
    }

    private List<ParamNode> filterToNodeInfo(List<? extends BaseNode> sourceObj) {
        List<ParamNode> result = new ArrayList<>();
        for (BaseNode baseNode : sourceObj) {
            if (baseNode instanceof ParamNode) {
                result.add((ParamNode) baseNode);
            }
        }
        return result;
    }

    private <T> List<T> calculateChain(List<ParamNode> from, RelationChain relationChains, String destObject) {
        List<RelationItem> itemList = relationChains.getRelationItems();
        Map<String, ParamNode> nodeInfoMap = from.stream().collect(Collectors.toMap(ParamNode::getNodeName, Function.identity()));
        for (RelationItem item : itemList) {
            RelationFunction function = getFunction(item);
            if (function == null) {
                throw new RuntimeException(String.format("不存在关系%s对应的实现", item));
            }
            ContextImpl context = new ContextImpl(item);
            List<RangeObj> sourceObjectList = item.getSourceObjectList();
            for (RangeObj rangeObj : sourceObjectList) {
                if (!nodeInfoMap.containsKey(rangeObj.getObjectName())) {
                    throw new RuntimeException(String.format("没找到对象%s的值", rangeObj.getObjectName()));
                }
                context.setValue(rangeObj.getObjectName(), nodeInfoMap.get(rangeObj.getObjectName()).getValue());
            }
            function.calculate(context);
            List<Object> value = context.getListValue(item.getDestObj());
            nodeInfoMap.put(item.getDestObj(), new ParamNode(item.getDestObj(), value));
        }
        return nodeInfoMap.get(destObject).value();
    }

    private boolean existFunc(RelationChain relationChains) {
        List<RelationItem> itemList = relationChains.getRelationItems();
        for (RelationItem relationItem : itemList) {
            RelationFunction function = getFunction(relationItem);
            if (function == null) {
                return false;
            }
        }
        return true;
    }

    private RelationFunction getFunction(RelationItem relationItem) {
        RelationFunction function = null;
        if (relationFunctionMap != null) {
            List<RelationFunction> relationFunctions = relationFunctionMap.get(relationItem);
            if (relationFunctions != null && !relationFunctions.isEmpty()) {
                function = relationFunctions.get(0);
                return function;
            }
        }
        if (functionProvider != null) {
            function = functionProvider.getFunction(relationItem.clone());
        }
        Map<String, RelationFunction> findResult = new HashMap<>();
        for (Map.Entry<String, FunctionProvider> providerEntry : functionProviderMap.entrySet()) {
            String key = providerEntry.getKey();
            FunctionProvider provider = providerEntry.getValue();
            RelationFunction match = provider.getFunction(relationItem);
            if (match != null) {
                findResult.put(key, match);
            }
        }
        if (findResult.size() == 1) {
            function = new ArrayList<>(findResult.values()).get(0);
        }
        if (findResult.size() > 1) {
            throw new RuntimeException(String.format("关系 %s 找到多个实现", relationItem));
        }
        return function;
    }

    @Override
    public void hideObject(List<String> objectName) {
        for (String name : objectName) {
            List<RelationItem> allFromItem = conceptManage.getGoRelationByObject(name);
            List<RelationItem> allToItem = conceptManage.getBackRelationByObject(name);
            List<RelationItem> deleteList = new ArrayList<>();
            Map<RelationItem, RelationFunction> addMap = new HashMap<>();

            if (CollectionUtils.isEmpty(allToItem)) {
                deleteList.addAll(allFromItem);
            } else {
                for (RelationItem toItem : allToItem) {

                    RelationFunction toFunction = getFunction(toItem);
                    List<String> sourceObjList = toItem.getSourceObjList();

                    List<RelationItem> possibleWay = allFromItem.stream().filter(item -> !sourceObjList.contains(item.getDestObj())).collect(Collectors.toList());

                    if (toFunction == null && !possibleWay.isEmpty()) {
                        throw new RuntimeException(String.format("关系%s的实现为空，无法隐去", toItem));
                    }
                    for (RelationItem fromItem : possibleWay) {
                        RelationFunction fromFunction = getFunction(fromItem);
                        merge(addMap, Ls.of(toItem, fromItem), Ls.of(toFunction, fromFunction));
                    }
                }
                deleteList.addAll(allToItem);
                deleteList.addAll(allFromItem);
            }
            ConceptManageImpl manage = (ConceptManageImpl) conceptManage;
            for (RelationItem relationItem : deleteList) {
                manage.removeRelationItem(relationItem.getSourceObjList(), relationItem.getDestObj());
            }

            if (!addMap.isEmpty()) {
                ArrayList<RelationItem> itemArrayList = new ArrayList<>(addMap.keySet());
                manage.addInstance(itemArrayList);

                for (Map.Entry<RelationItem, RelationFunction> functionEntry : addMap.entrySet()) {
                    RelationItem relationItem = functionEntry.getKey();
                    RelationFunction value = functionEntry.getValue();
                    registerFunction(relationItem.getSourceObjList(), relationItem.getDestObj(), value);
                }
            }
            manage.removeObject(name);
        }
    }

    private void merge(Map<RelationItem, RelationFunction> addMap, List<RelationItem> relationItems, List<RelationFunction> functions) {
        assert relationItems.size() == 2;
        assert functions.size() == 2;

        RelationItem first = relationItems.get(0);
        RelationItem second = relationItems.get(1);

        List<RangeObj> allSource = new ArrayList<>(first.getSourceObjectList());

        List<RangeObj> left = second.getSourceObjectList().stream().filter(aa -> !first.getDestObj().equals(aa.getObjectName()) && allSource.stream().noneMatch(bb -> bb.getObjectName().equals(aa.getObjectName()))).collect(Collectors.toList());

        allSource.addAll(left);

        RelationChain chain = new RelationChain();
        chain.setSourceObjectList(allSource);
        chain.setDestObject(second.getDestObject());
        chain.setRelationItems(relationItems);
        chain.fixDestObjRange();
        RelationItem relationItem = chain.toRelationItem();

        RelationFunction relationFunction = new MergeFuncImpl(first, second, functions.get(0), functions.get(1));
        addMap.put(relationItem, relationFunction);
    }

}
